///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
#include <Model/MeshData.h>
#include <Model/VertexBuffer.h>
#include <Model/VertexIndexBuffer.h>
#include <Material/Material.h>
#include <Model/MeshManager.h>
#include <Scene/Culling/AABBox.h>
#include <Scene/Culling/Sphere.h>
#include <SettingsManager.h>
#include <Scene/Object.h>
#include <Profiling/ProfilingManager.h>

//-----------------------------------------------------------------------------

MeshData::MeshData( std::string name )
{
	m_meshName = name;

	m_obb = NULL;
	m_sphere = NULL;

	MeshManager::GetInstance()->AddMesh( this );
}

//-----------------------------------------------------------------------------

MeshData::~MeshData()
{
	for(std::vector<MeshBuffers>::iterator iter = m_buffers.begin();
		iter != m_buffers.end();
		iter++ )
	{
		SAFE_DEL( iter->vertexBuffer );
		SAFE_DEL( iter->indexBuffer );
	}

	SAFE_DEL( m_obb );
	SAFE_DEL( m_sphere );
}

//-----------------------------------------------------------------------------

void	MeshData::AddMeshBuffers( MeshBuffers &meshBuf )
{
	m_buffers.push_back( meshBuf );
}

//-----------------------------------------------------------------------------

void	MeshData::Draw() 
{
	MGL_PROFILE("MeshData::draw");

	bool	depthRender = SettingsManager::GetInstance()->GetFlag(MGLS_RENDER_DEPTH);

	VertexBuffer *vb = NULL;
	VertexIndexBuffer *vi = NULL;
	std::vector<VertexInfo>	vertexComponents;
	std::vector<VertexInfo>::iterator viIter;

	for(std::vector<MeshBuffers>::iterator iter = m_buffers.begin();
		iter != m_buffers.end();
		iter++ )
	{
		vb = iter->vertexBuffer;
		vi = iter->indexBuffer;

		vertexComponents = vb->GetComponents();
		u8*	data = (u8*)vb->GetDataPtr();
		u32 noComponents = vb->GetNoComponents();
		u8 componentSize = vb->GetComponentSize();

		if( vi )
		{
			//indexed

#if defined(__MGL_USE_GL_POINTER_CALLS__)
			for( viIter = vertexComponents.begin(); viIter != vertexComponents.end(); viIter++ )
			{
				if( !depthRender || (*viIter).type == eVDT_POSITION )
				{
					sendDataPointer( *viIter, data, componentSize );
				}
			}

			{
				MGL_PROFILE("glRangeElements");
				glDrawRangeElements( GL_TRIANGLES, vi->GetRangeMin(), vi->GetRangeMax(), vi->GetNoComponents(), vi->GetGLComponentType(), vi->GetDataPtr() );
				GL_CHECK_ERRORS();
			}
#else

			u32 inoComponents = vi->GetNoComponents();
			u32	comp;

			glBegin( GL_TRIANGLES );
			GL_CHECK_ERRORS();

			for( u32 i = 0; i < inoComponents; i++ )
			{
				comp = vi->GetIndex( i );
				for( viIter = vertexComponents.begin(); viIter != vertexComponents.end(); viIter++ )
				{
					if( !depthRender || (*viIter).type == eVDT_POSITION )
					{
						sendData( *viIter, &data[comp * componentSize] );
					}
				}
			}

			glEnd();
			GL_CHECK_ERRORS();
#endif
		}
		else
		{
			//nonindexed

#if defined(__MGL_USE_GL_POINTER_CALLS__)

			for( viIter = vertexComponents.begin(); viIter != vertexComponents.end(); viIter++ )
			{
				if( !depthRender || (*viIter).type == eVDT_POSITION )
				{
					sendDataPointer( *viIter, data, componentSize );
				}
			}

			{
				MGL_PROFILE("glDrawArrays");
				glDrawArrays ( GL_TRIANGLES, 0, noComponents );
				GL_CHECK_ERRORS();
			}

#else	//standard data sending 
			
			glBegin( GL_TRIANGLES );
			GL_CHECK_ERRORS();

			for( u32 i = 0; i < noComponents; i++ )
			{
				for( viIter = vertexComponents.begin(); viIter != vertexComponents.end(); viIter++ )
				{
					if( !depthRender || (*viIter).type == eVDT_POSITION )
					{
						sendData( *viIter, &data[i * componentSize] );
					}
				}
			}

			glEnd();
			GL_CHECK_ERRORS();
#endif
		}
	}

#if defined(DEBUG)
	if( m_sphere )
	{
		//m_sphere->draw();
	}
#endif
}

//-----------------------------------------------------------------------------

void	MeshData::sendData( VertexInfo& vi, u8* data )
{
	switch(vi.type)
	{
		//glVertex[2,3,4][f,d,i,s]v
	case eVDT_POSITION:
		{
			switch(vi.noComponents)
			{
			case 2:
				{
					switch(vi.componentType)
					{
					case eVCT_FLOAT:
						{
							GLfloat *v = (GLfloat*) &data[vi.offset];
							glVertex2fv( v );
						}
						break;

					case eVCT_DOUBLE:
						{
							GLdouble *v = (GLdouble*) &data[vi.offset];
							glVertex2dv( v );
						}
						break;

					case eVCT_SHORT:
						{
							GLshort *v = (GLshort*) &data[vi.offset];
							glVertex2sv( v );
						}
						break;

					case eVCT_INTEGER:
						{
							GLint *v = (GLint*) &data[vi.offset];
							glVertex2iv( v );
						}
						break;

					default:
						ASSERT( false && "Unknown data type!" );
					}
				}
				break;

			case 3:
				{
					switch(vi.componentType)
					{
					case eVCT_FLOAT:
						{
							GLfloat *v = (GLfloat*) &data[vi.offset];
							glVertex3fv( v );
						}
						break;

					case eVCT_DOUBLE:
						{
							GLdouble *v = (GLdouble*) &data[vi.offset];
							glVertex3dv( v );
						}
						break;

					case eVCT_SHORT:
						{
							GLshort *v = (GLshort*) &data[vi.offset];
							glVertex3sv( v );
						}
						break;

					case eVCT_INTEGER:
						{
							GLint *v = (GLint*) &data[vi.offset];
							glVertex3iv( v );
						}
						break;

					default:
						ASSERT( false && "Unknown data type!" );
					}
				}
				break;

			case 4:
				{
					switch(vi.componentType)
					{
					case eVCT_FLOAT:
						{
							GLfloat *v = (GLfloat*) &data[vi.offset];
							glVertex4fv( v );
						}
						break;

					case eVCT_DOUBLE:
						{
							GLdouble *v = (GLdouble*) &data[vi.offset];
							glVertex4dv( v );
						}
						break;

					case eVCT_SHORT:
						{
							GLshort *v = (GLshort*) &data[vi.offset];
							glVertex4sv( v );
						}
						break;

					case eVCT_INTEGER:
						{
							GLint *v = (GLint*) &data[vi.offset];
							glVertex4iv( v );
						}
						break;

					default:
						ASSERT( false && "Unknown data type!" );
					}
				}
				break;

			default:
				ASSERT( false && "Unknown number of components!" );
			}
		}
		break;

		//glColor[3,4][b,d,f,i,s,ub,uf,ui,us]v
	case eVDT_COLOR:
		{
			switch(vi.noComponents)
			{
			case 3:
				{
					switch(vi.componentType)
					{
					case eVCT_FLOAT:
						{
							GLfloat *v = (GLfloat*) &data[vi.offset];
							glColor3fv( v );
						}
						break;

					case eVCT_DOUBLE:
						{
							GLdouble *v = (GLdouble*) &data[vi.offset];
							glColor3dv( v );
						}
						break;

					case eVCT_SHORT:
						{
							GLshort *v = (GLshort*) &data[vi.offset];
							glColor3sv( v );
						}
						break;

					case eVCT_INTEGER:
						{
							GLint *v = (GLint*) &data[vi.offset];
							glColor3iv( v );
						}
						break;

					default:
						ASSERT( false && "Unknown data type!" );
					}
				}
				break;

			case 4:
				{
					switch(vi.componentType)
					{
					case eVCT_FLOAT:
						{
							GLfloat *v = (GLfloat*) &data[vi.offset];
							glColor4fv( v );
						}
						break;

					case eVCT_DOUBLE:
						{
							GLdouble *v = (GLdouble*) &data[vi.offset];
							glColor4dv( v );
						}
						break;

					case eVCT_SHORT:
						{
							GLshort *v = (GLshort*) &data[vi.offset];
							glColor4sv( v );
						}
						break;

					case eVCT_INTEGER:
						{
							GLint *v = (GLint*) &data[vi.offset];
							glColor4iv( v );
						}
						break;

					default:
						ASSERT( false && "Unknown data type!" );
					}
				}
				break;

			default:
				ASSERT( false && "Unknown number of components!" );
			}
		}
		break;

		//glNormal[3][b,d,f,i,s]v
	case eVDT_NORMAL:
		{
			switch(vi.noComponents)
			{

			case 3:
				{
					switch(vi.componentType)
					{
					case eVCT_FLOAT:
						{
							GLfloat *v = (GLfloat*) &data[vi.offset];
							glNormal3fv( v );
						}
						break;

					case eVCT_DOUBLE:
						{
							GLdouble *v = (GLdouble*) &data[vi.offset];
							glNormal3dv( v );
						}
						break;

					case eVCT_SHORT:
						{
							GLshort *v = (GLshort*) &data[vi.offset];
							glNormal3sv( v );
						}
						break;

					case eVCT_INTEGER:
						{
							GLint *v = (GLint*) &data[vi.offset];
							glNormal3iv( v );
						}
						break;

					default:
						ASSERT( false && "Unknown data type!" );
					}
				}
				break;

			default:
				ASSERT( false && "Unknown number of components!" );
			}
		}
		break;

		//glTexCoord[1,2,3,4][d,f,i,s]v
	case eVDT_TEXCOORD:
		{
			switch(vi.noComponents)
			{
			case 1:
				{
					switch(vi.componentType)
					{
					case eVCT_FLOAT:
						{
							GLfloat *v = (GLfloat*) &data[vi.offset];
							glTexCoord1fv( v );
						}
						break;

					case eVCT_DOUBLE:
						{
							GLdouble *v = (GLdouble*) &data[vi.offset];
							glTexCoord1dv( v );
						}
						break;

					case eVCT_SHORT:
						{
							GLshort *v = (GLshort*) &data[vi.offset];
							glTexCoord1sv( v );
						}
						break;

					case eVCT_INTEGER:
						{
							GLint *v = (GLint*) &data[vi.offset];
							glTexCoord1iv( v );
						}
						break;

					default:
						ASSERT( false && "Unknown data type!" );
					}
				}
				break;

			case 2:
				{
					switch(vi.componentType)
					{
					case eVCT_FLOAT:
						{
							GLfloat *v = (GLfloat*) &data[vi.offset];
							glTexCoord2fv( v );
						}
						break;

					case eVCT_DOUBLE:
						{
							GLdouble *v = (GLdouble*) &data[vi.offset];
							glTexCoord2dv( v );
						}
						break;

					case eVCT_SHORT:
						{
							GLshort *v = (GLshort*) &data[vi.offset];
							glTexCoord2sv( v );
						}
						break;

					case eVCT_INTEGER:
						{
							GLint *v = (GLint*) &data[vi.offset];
							glTexCoord2iv( v );
						}
						break;

					default:
						ASSERT( false && "Unknown data type!" );
					}
				}
				break;

			case 3:
				{
					switch(vi.componentType)
					{
					case eVCT_FLOAT:
						{
							GLfloat *v = (GLfloat*) &data[vi.offset];
							glTexCoord3fv( v );
						}
						break;

					case eVCT_DOUBLE:
						{
							GLdouble *v = (GLdouble*) &data[vi.offset];
							glTexCoord3dv( v );
						}
						break;

					case eVCT_SHORT:
						{
							GLshort *v = (GLshort*) &data[vi.offset];
							glTexCoord3sv( v );
						}
						break;

					case eVCT_INTEGER:
						{
							GLint *v = (GLint*) &data[vi.offset];
							glTexCoord3iv( v );
						}
						break;

					default:
						ASSERT( false && "Unknown data type!" );
					}
				}
				break;

			case 4:
				{
					switch(vi.componentType)
					{
					case eVCT_FLOAT:
						{
							GLfloat *v = (GLfloat*) &data[vi.offset];
							glTexCoord4fv( v );
						}
						break;

					case eVCT_DOUBLE:
						{
							GLdouble *v = (GLdouble*) &data[vi.offset];
							glTexCoord4dv( v );
						}
						break;

					case eVCT_SHORT:
						{
							GLshort *v = (GLshort*) &data[vi.offset];
							glTexCoord4sv( v );
						}
						break;

					case eVCT_INTEGER:
						{
							GLint *v = (GLint*) &data[vi.offset];
							glTexCoord4iv( v );
						}
						break;

					default:
						ASSERT( false && "Unknown data type!" );
					}
				}
				break;

			default:
				ASSERT( false && "Unknown number of components!" );
			}
		}
		break;

	default:
		ASSERT( false && "Unknown vertex data type!" );
	}
	GL_CHECK_ERRORS();
}

//-----------------------------------------------------------------------------

#if defined(__MGL_USE_GL_POINTER_CALLS__)
void	MeshData::sendDataPointer( VertexInfo& vi, u8* data, u8 stride )
{
	MGL_PROFILE("MeshData::sendDataPointer");

	GLenum type = GL_FLOAT;

	switch(vi.componentType)
	{
	case eVCT_FLOAT:
		type = GL_FLOAT;
		break;

	case eVCT_DOUBLE:
		type = GL_DOUBLE;
		break;

	case eVCT_INTEGER:
		type = GL_INT;
		break;

	case eVCT_SHORT:
		type = GL_SHORT;
		break;

	default:
		ASSERT( false && "Unknown component type!" );
	}

	switch(vi.type)
	{
	case eVDT_POSITION:
		{
			glEnableClientState(GL_VERTEX_ARRAY);
			GL_CHECK_ERRORS();
			glVertexPointer( vi.noComponents, type, stride, &data[vi.offset] );
			GL_CHECK_ERRORS();
		}
		break;

	case eVDT_COLOR:
		{
			glEnableClientState(GL_COLOR_ARRAY);
			GL_CHECK_ERRORS();
			glColorPointer( vi.noComponents, type, stride, &data[vi.offset] );
			GL_CHECK_ERRORS();
		}
		break;

	case eVDT_NORMAL:
		{
			ASSERT( vi.noComponents == 3 );
			glEnableClientState(GL_NORMAL_ARRAY);
			GL_CHECK_ERRORS();
			glNormalPointer( type, stride, &data[vi.offset] );
			GL_CHECK_ERRORS();
		}
		break;

	case eVDT_TEXCOORD:
		{
			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
			GL_CHECK_ERRORS();
			glTexCoordPointer( vi.noComponents, type, stride, &data[vi.offset] );
			GL_CHECK_ERRORS();
		}
		break;

	default:
		ASSERT( false && "Unknown vertex data type!" );
	}

}
#endif

//-----------------------------------------------------------------------------

bool	MeshData::HasBoundingElement()
{
	return ( m_obb != NULL || m_sphere != NULL );
}

//-----------------------------------------------------------------------------

void	MeshData::getMinMax( f32& minx, f32& miny, f32& minz, f32& maxx, f32& maxy, f32& maxz )
{
	VertexBuffer* vb = NULL;
	VertexIndexBuffer* vib = NULL;

	for(u16 i = 0; i < m_buffers.size(); i++ )
	{
		vib = m_buffers[i].indexBuffer;
		vb = m_buffers[i].vertexBuffer;
		
		std::vector<VertexInfo>	vertexComponents;
		std::vector<VertexInfo>::iterator viIter;
		vertexComponents = vb->GetComponents();
		u8 off = 0;
		u8 noco = 0;
		u8 type = 0;
		u8 tsize = 0;
		u8 cosize = vb->GetComponentSize();
		for( viIter = vertexComponents.begin(); viIter != vertexComponents.end(); viIter++ )
		{
			if( (*viIter).type == eVDT_POSITION )
			{
				off = (*viIter).offset;
				noco = (*viIter).noComponents;
				type = (*viIter).componentType;
				tsize = (*viIter).sizeComponent;
				break;
			}
		}

		ASSERT( noco == 3 );

		if(vib)
		{
			//indexed

			u32 inoComponents = vib->GetNoComponents();
			u8 *dataU = (u8*)vb->GetDataPtr();//TODO - de facut pentru toate tipurile de componente
			u32 x = vib->GetIndex( 0 );
			f32 *data = (f32*)&dataU[ x * cosize + off ];
			minx = maxx = data[0];
			miny = maxy = data[1];
			minz = maxz = data[2];
			f32 val;
			for( u32 i = 0; i < inoComponents; i++ )
			{
				x = vib->GetIndex( i );
				data = (f32*)&dataU[ x * cosize + off ];
				val = data[0];
				if( val < minx ) minx = val;
				if( val > maxx ) maxx = val;

				val = data[1];
				if( val < miny ) miny = val;
				if( val > maxy ) maxy = val;

				val = data[2];
				if( val < minz ) minz = val;
				if( val > maxz ) maxz = val;
			}
		}
		else
		{
			//nonindexed

			u32 s = vb->GetNoComponents();
			u8 *dataU = (u8*)vb->GetDataPtr();//TODO - de facut pentru toate tipurile de componente
			f32 val;
			f32 *data = (f32*)&dataU[ off ];
			minx = maxx = data[0];
			miny = maxy = data[1];
			minz = maxz = data[2];
			for( u32 x = 0; x < s; x++ )
			{
				data = (f32*)&dataU[ x * cosize + off ];
				val = data[0];
				if( val < minx ) minx = val;
				if( val > maxx ) maxx = val;

				val = data[1];
				if( val < miny ) miny = val;
				if( val > maxy ) maxy = val;

				val = data[2];
				if( val < minz ) minz = val;
				if( val > maxz ) maxz = val;
			}
		}
	}
}

//-----------------------------------------------------------------------------

void	MeshData::generateOBB()
{
	if( !m_obb )
	{
		m_obb = MGL_NEW AABBox();
	}
	f32 minx = 0, miny = 0, minz = 0,
		maxx = 0, maxy = 0, maxz = 0;

	getMinMax( minx, miny, minz, maxx, maxy, maxz );

	m_obb->setBox( Vec3f( minx, miny, minz ), maxx - minx, maxy - miny, maxz - minz );
}

//-----------------------------------------------------------------------------

AABBox*		MeshData::GetOBB()
{
	return m_obb;
}

//-----------------------------------------------------------------------------

void	MeshData::generateBoundingSphere()
{
	if( !m_sphere )
	{
		m_sphere = MGL_NEW Sphere();
	}
	f32 minx = 0, miny = 0, minz = 0,
		maxx = 0, maxy = 0, maxz = 0;

	getMinMax( minx, miny, minz, maxx, maxy, maxz );

	m_sphere->setSphere( Vec3f( (minx + maxx) / 2.0f, (miny + maxy) / 2.0f, (minz + maxz) / 2.0f ), MAX( maxx - minx, MAX( maxy - miny, maxz - minz ) ) / 2.0f );
}

//-----------------------------------------------------------------------------

Sphere*		MeshData::GetBoundingSphere()
{
	return m_sphere;
}

//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
